package com.hzeport.bgs22emduty.service.impl;

import com.hzeport.bgs22emduty.config.exception.HdcException;
import com.hzeport.bgs22emduty.entity.WarningLog;
import com.hzeport.bgs22emduty.enums.OperateTypeEnum;
import com.hzeport.bgs22emduty.outbound.WarningLogRepository;
import com.hzeport.bgs22emduty.service.WarningLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class WarningLogServiceImpl implements WarningLogService {

    private final RedisTemplate<String, String> redisTemplate;
    private static final String WARNING_KEY_PREFIX = "WARNING_ACCESS_CNT_";
    private static final String FORBID_KEY_PREFIX = "FORBID_";
    private static final Long ACCESS_CNT_LIMIT = 50L;
    private static final Long TIME_WINDOW = 60L;
    private static final Long FORBID_TIME = 10L;

    private final WarningLogRepository warningLogRepository;

    @Override
    public void accessCntValidate(String path, String userNo) {
        final String WARNING_KEY = WARNING_KEY_PREFIX + userNo + "_" + path;
        final String FORBID_KEY = FORBID_KEY_PREFIX + userNo + "_" + path;
        // 调用次数，每次调用+1， 返回本次+1后的值
        Long cnt = redisIncr(WARNING_KEY);
        if (warning(FORBID_KEY, cnt)) {
            final WarningLog warningLog = new WarningLog();
            warningLog.setOperateType(OperateTypeEnum.FREQUENTLY_ACCESS.getCode());
            warningLog.setReqPath(path);
            warningLog.setOperatorCode(userNo);
            warningLogRepository.save(warningLog);
            throw new HdcException("调用接口频率过高," + FORBID_TIME + "分钟内禁止调用");
        }
    }


    private Long redisIncr(String key) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", TIME_WINDOW, TimeUnit.SECONDS);
        // 第一次调用，返回初始值
        if (Boolean.TRUE.equals(success)) {
            return 1L;
        }
        // 后续调用：值 +1， 不更新过期时间
        return redisTemplate.opsForValue().increment(key);
    }


    private boolean warning(String forbidKey, long cnt) {
        if (cnt <= ACCESS_CNT_LIMIT) {
            return false;
        }
        redisTemplate.opsForValue().setIfAbsent(forbidKey, "1", FORBID_TIME, TimeUnit.MINUTES);
        return true;
    }

}
